package com.sky.aspect;


import com.sky.annotation.AutoFill;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

//这里声明他是一个AOP类 且AOP类是一个bean所以我们要让他加入到IOC容器中(本质是动态代理)
@Aspect
@Component
@Slf4j
public class AutoFillAspect {
    //这里要对我们的mapper层进行相关的处理(公共字段)
    //这里我们设置切入点表达式(设置了既要满足注解又要在我们设定的切入点中)
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void autoFillpointCut() {}//无需写方法体仅仅起到绑定切入点表达式和通知作用
    //这里我们设置我们的前置通知(这里我们利用了连接点参数来实现获取目标对象的参数)
    @Before("autoFillpointCut()")
    public void autofill(JoinPoint joinPoint) {
        log.info("开始进行公共字段的填充");
        //这里需要我们先获取到我们自定义注解中给属性赋的值 来判断填入什么公共的字段(见笔记分为两种)
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象（利用强转）
        AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获取方法上的注解对象
        OperationType operationType = autoFill.value();//获取数据库操作类型
        //然后获取我们要操作的实体对象
        Object[] args = joinPoint.getArgs();//获得所有的参数
        if(args == null || args.length == 0){
            //如果当前方法没有参数
            return;
        }
        Object entity = args[0];//获取方法参数的实体对象
        //准备要赋值的东西(使用我们封装的工具类)
        LocalDateTime ldt = LocalDateTime.now();
        long currentid= BaseContext.getCurrentId();
        //然后对公共属性赋值（获取要赋值的值 然后分类型赋值）
        if(operationType == OperationType.INSERT){
            //这里如果是我们的插入类型我们就对4个公共字段赋值
            try {
                //这里我们使用反射获得到我们要使用的实体类的方法来给实体类对象赋值
                Method setCreateTime = entity.getClass().getDeclaredMethod("setCreateTime", LocalDateTime.class);
                Method setCreateUser = entity.getClass().getDeclaredMethod("setCreateUser", Long.class);
                Method setUpdateTime = entity.getClass().getDeclaredMethod("setUpdateTime", LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod("setUpdateUser", Long.class);
                //通过反射为对象属性赋值
                setCreateTime.invoke(entity, ldt);
                setCreateUser.invoke(entity, currentid);
                setUpdateTime.invoke(entity, ldt);
                setUpdateUser.invoke(entity, currentid);
            } catch (Exception e) {//处理可能的异常
                e.printStackTrace();
            }
        }else {
            //如果是其他的更新类型我们只需要改变2个公共字段的值
            try {
                Method setUpdateTime = entity.getClass().getDeclaredMethod("setUpdateTime", LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod("setUpdateUser", Long.class);
                //通过反射为对象属性赋值
                setUpdateTime.invoke(entity, ldt);
                setUpdateUser.invoke(entity, currentid);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }
}
